package com.snlab;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;

public class APKeep {
    public ArrayList<Rule> rules = new ArrayList<>();
    public BDDEngine bddEngine = new BDDEngine(32);
    public BiMap<Port, Integer> ppm = HashBiMap.create();
    public HashMap<String, Port> ports = new HashMap<>();
    private Comparator<Rule> comp = (Rule lhs, Rule rhs) -> rhs.prefix - lhs.prefix;

    public APKeep() {
        ppm.put(getPort("drop"), 1);
    }

    public Port getPort(String name) {
        if (ports.get(name) == null) {
            ports.put(name, new Port(name));
        }
        return ports.get(name);
    }

    public void insert(Rule rule) {
        ArrayList<Change> changes = new ArrayList<>();
        
        for (Rule rp : rules) {
            if (bddEngine.and(rp.hit, rule.hit) != 0) {
                if (rp.prefix > rule.prefix) {
                    rule.hit = bddEngine.and(rule.hit, bddEngine.not(rp.hit));
                }
                if (rp.prefix < rule.prefix) {
                    if (rp.port != rule.port) {
                        int h = bddEngine.and(rp.hit, rule.hit);
                        Change change = new Change(h, rp.port, rule.port);
                        changes.add(change);
                    }
                    rule.hit = bddEngine.and(rule.hit, bddEngine.not(rp.hit));
                    rp.hit = bddEngine.and(rp.hit, bddEngine.not(rule.hit));
                }
            }
        }
        rules.add(0, rule);
        // rules.sort(comp);
    
    
        for (Change change : changes) {
            int p = ppm.get(change.from);
            // assert(bddEngine.and(change.pred, p) != 0);
            // assert(bddEngine.and(change.pred, p) != p);
            Port port = ppm.inverse().get(p);
            ppm.put(port, bddEngine.and(p, bddEngine.not(change.pred)));
            if (ppm.get(change.to) == null) {
                ppm.put(change.to, 0);
            }
            ppm.put(change.to, bddEngine.or(ppm.get(change.to), change.pred));
        }
    }

}
